﻿--[[
	This file is part of FlexBar3.

	FlexBar3 is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	FlexBar3 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with FlexBar3.  If not, see <http://www.gnu.org/licenses/>.
]]
-- Local upvalues
local tinsert = tinsert;
local tremove = tremove;
local assert = assert;
local type = type;

local EventClass = FlexBar3:GetModule("Button"):NewButtonClass("Event", 1);

local function RemoveValueFromTable(Table, Value)
	if(Table == nil) then return false; end
	if(Value == nil) then return false; end
	-- Infinite for loop, break when we get a nil value as the size of the table will change during the loop its not a reliable thing to use.
	for i = 1, math.huge do
		local CurrentValue = Table[i];
		if(CurrentValue == Value) then
			i = i - 1;
			tremove(Table, i);
		elseif(CurrentValue == nil) then
			return true, i;
		end
	end
end

local function EventHandler(self, Callback, Event, ...)
	local CurrentEventTable = self.EventTable[Event];
	for i = 1, #CurrentEventTable do
		CurrentEventTable[i](self, Event, ...);
	end
end

function EventClass:Load()
	self.ScriptTable = {};
	self.EventTable = {};
	self.CallbackTable = {};
	self:RegisterScript("OnEvent", EventHandler);
end

function EventClass:Disable()
	self.Frame:UnregisterAllEvents();
	self:UnregisterScript("OnEvent", EventHandler);
end

function EventClass:RegisterEvent(Event, Callback)
	assert(type(Event) == "string", ("Event must be a string, it was a [%s]"):format(type(Event)), 2);
	if(type(Callback) == "string") then Callback = self[Callback]; end
	assert(type(Callback) == "function", ("Callback must be a function or the name of an existing function, it was a [%s]"):format(type(Callback)), 2);
	if(not self.EventTable[Event]) then
		self.EventTable[Event] = {};
	end
	tinsert(self.EventTable[Event], Callback);
	self.Frame:RegisterEvent(Event);
end


function EventClass:UnregisterEvent(Event, Callback)
	assert(type(Event) == "string", ("Event must be a string, it was a [%s]"):format(type(Event)), 2);
	if(type(Callback) == "string") then Callback = self[Callback]; end
	assert(type(Callback) == "function", ("Callback must be a function or the name of an existing function, it was a [%s]"):format(type(Callback)), 2);
	RemoveValueFromTable(self.EventTable[Event], Callback);
end

function EventClass:RegisterScript(Script, Callback)
	assert(type(Script) == "string", ("Script must be a string, it was a [%s]"):format(type(Script)), 2);
	if(type(Callback) == "string") then Callback = self[Callback]; end
	assert(type(Callback) == "function", ("Callback must be a function or the name of an existing function, it was a [%s]"):format(type(Callback)), 2);
	if(not self.ScriptTable[Script]) then
		local CurrentScriptTable = {};
		self.ScriptTable[Script] = CurrentScriptTable;
		self.Frame:SetScript(Script,
		function (this, ...)
			for i = 1, #CurrentScriptTable do
				CurrentScriptTable[i](self, Script, ...);
			end
		end);
	end
	tinsert(self.ScriptTable[Script], Callback);
end


function EventClass:UnregisterScript(Script, Callback)
	assert(type(Script) == "string", ("Script must be a string, it was a [%s]"):format(type(Script)), 2);
	if(type(Callback) == "string") then Callback = self[Callback]; end
	assert(type(Callback) == "function", ("Callback must be a function or the name of an existing function, it was a [%s]"):format(type(Callback)), 2);
	RemoveValueFromTable(self.ScriptTable[Script], Callback);
end

function EventClass:RegisterCallback(Name, Callback)
	assert(type(Name) == "string", ("Name must be a string, it was a [%s]"):format(type(Name)), 2);
	if(type(Callback) == "string") then Callback = self[Callback]; end
	assert(type(Callback) == "function", ("Callback must be a function or the name of an existing function, it was a [%s]"):format(type(Callback)), 2);
	if(not self.CallbackTable[Name]) then
		self.CallbackTable[Name] = {};
	end
	tinsert(self.CallbackTable[Name], Callback);
end

function EventClass:UnregisterCallback(Name, Callback)
	assert(type(Name) == "string", ("Script must be a string, it was a [%s]"):format(type(Name)), 2);
	if(type(Callback) == "string") then Callback = self[Callback]; end
	assert(type(Callback) == "function", ("Callback must be a function or the name of an existing function, it was a [%s]"):format(type(Callback)), 2);
	RemoveValueFromTable(self.CallbackTable[Name], Callback);
end

function EventClass:TriggerCallback(Name, ...)
	local CurrentCallbackTable = self.CallbackTable[Name];
	if(CurrentCallbackTable) then
		for i = 1, #CurrentCallbackTable do
			CurrentCallbackTable[i](self, Name, ...);
		end
	end
end
